package com.jwd.avmanager.server;

import com.jwd.avmanager.constant.Cmd;
import com.jwd.avmanager.entity.DecodePacket;
import com.jwd.avmanager.util.ByteUtil;
import com.jwd.avmanager.util.DebugLogger;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;


public class CustomMessageDecoder extends ByteToMessageDecoder {
    private final static int HEAD_LENGTH = 12;
    private List<Short> allCmds = new ArrayList<>();
    static int capacity = 50000 * 4;
    private ByteBuf circleBuffer = Unpooled.buffer(capacity, capacity);

    public CustomMessageDecoder() {
        super();
        //TODO 每新增一个协议，就需要添加到这个列表，用来做合法性检测
        allCmds.add(Cmd.CMD_IN_UNIVERSAL_REPLY);
        allCmds.add(Cmd.CMD_IN_LOGIN);
        allCmds.add(Cmd.CMD_IN_HEARTBEAT);
        allCmds.add(Cmd.CMD_IN_HEARTBEAT_V2);
        allCmds.add(Cmd.CMD_IN_CANCEL_TERMINAL);
        allCmds.add(Cmd.CMD_IN_REQUEST_GPS);
        allCmds.add(Cmd.CMD_IN_TALKBACK_GROUP);
        allCmds.add(Cmd.CMD_IN_TALKBACK_PERSONNEL);
        allCmds.add(Cmd.CMD_IN_CALL_PERSONNEL_LIST);
        allCmds.add(Cmd.CMD_IN_CALL_PERSONNEL_LIST2);
        allCmds.add(Cmd.CMD_IN_IEMI_REGISTER);
        allCmds.add(Cmd.CMD_IN_VIDEO_PERSONNEL_LIST);
        allCmds.add(Cmd.CMD_IN_TARGET_STATE);
        allCmds.add(Cmd.CMD_IN_JOIN_WEBRTC);
        allCmds.add(Cmd.CMD_IN_QUIT_WEBRTC);
        allCmds.add(Cmd.CMD_IN_SOS_EMERGENCY);
        allCmds.add(Cmd.CMD_IN_WEBRTC_MESSAGE);
        allCmds.add(Cmd.CMD_IN_GET_FTP_SERVER);
        allCmds.add(Cmd.CMD_IN_UPLOADING_FILE);
        allCmds.add(Cmd.CMD_IN_EXAMINE_FILE);
        allCmds.add(Cmd.CMD_IN_SEND_SHOOT_FILES);
        allCmds.add(Cmd.CMD_IN_GET_UPGRADE_FILE);
        allCmds.add(Cmd.CMD_IN_LOGIN_V2);
    }

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        byte[] data = new byte[in.readableBytes()];
        in.readBytes(data);
//        DebugLogger.debug("数据长度："+ data.length);
        int remainLen = circleBuffer.capacity() - circleBuffer.writerIndex();
        if (remainLen >= data.length) {
            //1.如果长度够用，直接写入
            circleBuffer.writeBytes(data);
        } else {
            //2.如果长度不够用，那么就要开启循环的写入方式
            byte[] remainData = new byte[circleBuffer.readableBytes()];
            circleBuffer.readBytes(remainData);
            circleBuffer.resetReaderIndex();
            circleBuffer.resetWriterIndex();
            circleBuffer.writeBytes(remainData);
            circleBuffer.writeBytes(data);

        }
        //解析缓冲区
        //读取头部，判断帧长度
        if (circleBuffer.readableBytes() < HEAD_LENGTH) {
            //字数长度不够
            return;
        }
        byte[] header = new byte[HEAD_LENGTH];
        circleBuffer.getBytes(circleBuffer.readerIndex(), header);
        ByteBuf headerBuf = Unpooled.copiedBuffer(header);
        short cmd = headerBuf.readShortLE();
        short serialNo = headerBuf.readShortLE();
        int frameLen = headerBuf.readIntLE();
        int readableLen = 0;
        readableLen = circleBuffer.writerIndex() - circleBuffer.readerIndex();
        DebugLogger.info("decode headerBuf cmd：" + cmd + " serialNo" + serialNo + " frameLen" + frameLen);
        while (readableLen >= frameLen) {
            byte[] readData = new byte[frameLen];
            boolean isCircle = false;//是否发生了循环
            isCircle = circleBuffer.writerIndex() - circleBuffer.readerIndex() < 0;
            if (!isCircle) {
                //正常读
                circleBuffer.readBytes(readData);
            } else {
                //判断尾部长度进行读
                int endLen = circleBuffer.capacity() - circleBuffer.readerIndex();
                if (endLen >= frameLen) {
                    //正常读
                    circleBuffer.readBytes(readData);
                } else {
                    //尾巴不够长,先把尾巴读完，然后重头开始读
                    circleBuffer.readBytes(readData, 0, endLen);
                    circleBuffer.resetReaderIndex();
                    circleBuffer.readBytes(readData, endLen, readData.length - endLen);
                }
            }
            DebugLogger.info("decode 完整的数据：" + ByteUtil.getHexStr(readData));
            DecodePacket packet = parseMsg(readData);
            //校验合法性
            boolean isValid = checkPacket(packet);
            if (isValid) {
                //发送给消息处理器
//                DebugLogger.debug("收到一个包，发送给解析器");
                out.add(packet);
            } else {
                throw new RuntimeException("invalid jwd client packet!");
            }
            readableLen -= frameLen;
            if (readableLen >= 12) {
                byte[] nextHeader = new byte[HEAD_LENGTH];
                circleBuffer.getBytes(circleBuffer.readerIndex(), nextHeader);
//                DebugLogger.debug("next header:"+Arrays.toString(header));
                //解析头部
                ByteBuf nextHeaderBuf = Unpooled.copiedBuffer(nextHeader);
                short nextCmd = nextHeaderBuf.readShortLE();
                short nextSerialNo = nextHeaderBuf.readShortLE();
                frameLen = nextHeaderBuf.readIntLE();
//                DebugLogger.debug("next frame len:"+frameLen);
            }
        }
    }

    private boolean checkPacket(DecodePacket packet) {
        boolean ret = false;
        if (allCmds.contains(packet.getCmdType())) {
            ret = true;
//            DebugLogger.debug("合法");
        } else {
            DebugLogger.info("checkPacket 非合法指令:" + packet.getCmdType());
        }
        return ret;
    }

    /**
     * 解析包
     *
     * @param bytes
     * @return
     */
    private DecodePacket parseMsg(byte[] bytes) {
        //根据协议文档进行解封装
        byte[] cmdTypeBytes = new byte[2];
        byte[] serialBytes = new byte[2];
        byte[] cmdLenBytes = new byte[4];
        byte[] devIdxBytes = new byte[4];
        System.arraycopy(bytes, 0, cmdTypeBytes, 0, 2);
        System.arraycopy(bytes, 2, serialBytes, 0, 2);
        System.arraycopy(bytes, 4, cmdLenBytes, 0, 4);
        System.arraycopy(bytes, 8, devIdxBytes, 0, 4);
        short cmdType = ByteUtil.bytesToShort(cmdTypeBytes);
        short serial = ByteUtil.bytesToShort(serialBytes);
        int cmdLen = ByteUtil.bytesToInt(cmdLenBytes);
        int devIdx = ByteUtil.bytesToInt(devIdxBytes);
        byte[] body = new byte[bytes.length - HEAD_LENGTH];
        System.arraycopy(bytes, HEAD_LENGTH, body, 0, bytes.length - HEAD_LENGTH);
        DecodePacket packet = new DecodePacket(cmdType, serial, cmdLen, devIdx, bytes, body);
        return packet;
    }
}
